#include "catalog.h"
#include "query.h"
#include "index.h"
#include <string.h>
#include <stdlib.h>

// Implements the index select operator
Status Operators::IndexSelect(const string & result,      // name of the output relation
  	                      const int projCnt,          // number of attributes in the projection
                              const AttrDesc projNames[], // The projection list (as AttrDesc)
                              const AttrDesc *attrDesc,   // the attribute in the selection predicate
                              const Operator op,          // the predicate operation
                              const void *attrValue,      // a pointer to the literal value in the predicate
                              const int reclen)           // length of a tuple in the result relation
{
    cout << "Algorithm: Index Select" << endl;
   
    Status status;
    RID nextRid;
    RID newRid;
    AttrDesc * relattrs;
    int aCount;
    Record record;
    Record resultRec;
    resultRec.length = reclen;
         
	 
    Index * scan;    
    HeapFile resultHeap(result, status);
    
    if(status != OK)
    {
        return status;
    }
    
    HeapFileScan startHeap(projNames[0].relName, status);

    if(status != OK)
    {
        return status;
    }
    //Get information about the relation 
    status = attrCat->getRelInfo(string(attrDesc->relName), aCount, relattrs);
    
    if(status != OK)
    {
        delete[] relattrs;
        return status;
    }
    
    //Begin the index scan
    scan = new Index(string(attrDesc->relName), attrDesc->attrOffset, attrDesc->attrLen,
    			       (Datatype)attrDesc->attrType, 0, status);

    if(status != OK)
    {
        if(scan)
        	delete scan;
        delete[] relattrs;
        return status;
    }

    status = scan->startScan(attrValue);
    
    if(status != OK)
    {
	delete[] relattrs;
	delete scan;
        return status;
    }
    
    status = scan->scanNext(nextRid);
         
    if(status != OK)
    {
	delete[] relattrs;
	delete scan;
        return status;
    }            
    
    //Go through the Index On the attribute
    while(status == OK)
    {
        status = startHeap.getRandomRecord(nextRid, record);

        if(status != OK)
        {
	    delete[] relattrs;
	    delete scan;
            return status;
         }
    
        //Allocate memory for the new tuple
	resultRec.data = malloc(record.length);

	if(!resultRec.data)
	{
	    delete[] relattrs;
	    delete scan;
	    return INSUFMEM;
	}

        //Loop through projection list and add attributes to new tuple
	for(int i = 0; i < projCnt; i++)
	{
            for(int j = 0; j < aCount; j++)
	    {
	        if(strcmp(projNames[i].relName, relattrs[j].relName) == 0 && 
	           strcmp(projNames[i].attrName, relattrs[j].attrName) == 0)
	        {
	            if(projNames[i].attrType != relattrs[j].attrType)
	            {
	                free(resultRec.data);
			delete[] relattrs;
	                delete scan;
			return ATTRTYPEMISMATCH;
	            }
	 
	            memcpy(((char *)resultRec.data)+projNames[i].attrOffset, 
		         ((char *)record.data)+relattrs[j].attrOffset, projNames[i].attrLen);
	            break;
		}
	    }
	}  

	//Add the newly formed tuple to the result heapfile
        status = resultHeap.insertRecord(resultRec, newRid);
	 
	if(status != OK)
        {
	    delete[] relattrs;
	    delete scan;
            return status;
         } 
	
	status = scan->scanNext(nextRid);
    }
  
    delete relattrs;
    delete scan;
    
    if(status != NOMORERECS)
    {
	return status;
    }
 
  return OK;
}

